package com.quality.mybatis.utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;

/**
 * mybatis工具类
 */
public class SqlSessionUtil {
    // 工具类的构造方法一般都是私有化的
    // 防止外界直接new这个工具类的对象
    // 里面的方法一般都是静态方法，可以直接调用
    private SqlSessionUtil(){}

    private static SqlSessionFactory sqlSessionFactory;
    private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<>();

    // 所以把下面的代码提前，让他在类加载时执行
    // 这样就不会重复执行
    static {
        try {
            sqlSessionFactory = new SqlSessionFactoryBuilder().
                    build(Resources.getResourceAsStream("mybatis-config.xml"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取会话对象
     * 外界调用的也是这个方法，只不过这个方法里面的sqlSessionFactory是通过前面的静态代码块创建出来的
     */
    public static SqlSession openSession(){
        /**
         * 这个就和javaweb后面的手撕threadlocal源码一样，
         * 在工具类里面和线程进行绑定
         * （因为工具类一般都是获取一些连接对象、sqlSession对象之类的，
         * 这些对象在不同的层需要保持一致才可以保证事务）
         */
        SqlSession sqlSession = threadLocal.get();
        if (sqlSession == null) {
            sqlSession = sqlSessionFactory.openSession();
            // 将sqlSession对象绑定到当前线程
            threadLocal.set(sqlSession);
        }
        return sqlSession;
    }

    /**
     * 关闭sqlSession，从当前线程中移除sqlSession对象
     * @param sqlSession
     */
    public static void close(SqlSession sqlSession){
        if (sqlSession != null) {
            sqlSession.close();
        }
        // 因为tomcat支持线程池，不移除的话会出现线程重复使用的情况
        /**
         * 这个方法不用传入参数，底层的原理可以看javaweb后面的手撕ThreadLocal源码
         * 底层其实是通过map来实现线程和泛型对象绑定的。key是当前线程，value是泛型对象
         * 底层源码直接通过“threadMap.remove(Thread.currentThread());”即可实现移除
         */
        threadLocal.remove();
    }
}
